﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Xml.XPath;
using Rodunu.Movies.Services.Interfaces.Netflix;
using Rodunu.Movies.Services.Models.Netflix;
using Rodunu.Movies.Services.Models.Netflix.Attributes;
using Rodunu.Movies.Contracts;
using Rodunu.Movies.Services.NetFlix.DataProvider.NFService;
using Rodunu.Movies.Services.NetFlix.Logic;

namespace Rodunu.Movies.Services.NetFlix.DataProvider
{
    public class SerializationHelper
    {
        #region Private Members

        private readonly NetflixService _netflixService;

        #endregion

        #region Constructors

        public SerializationHelper(NetflixService netflixService)
        {
            _netflixService = netflixService;
        }

        #endregion

        #region Public Methods

        public object FromXml(Type T, string xml)
        {
            return FromXmlWork(T, xml, true);
        }

        #endregion

        #region Private Methods

        private void SerializationHelper_TitlesAdded(object sender, PropertyChangedEventArgs e)
        {
            if (((Item)sender).Formats.Href != null)
            {
                var parameters = new NameValueCollection();
                try
                {
                    ((Item)sender).FormatAvailability = (FormatAvailabilityList)
                    FromXmlWork(typeof(FormatAvailabilityList),
                        new StreamReader(_netflixService.Get(((Item)sender).Formats.Href, parameters).GetResponseStream()).ReadToEnd(), false);
                }
                catch (Exception ex)
                {
                }
            }
        }

        private object FromXmlWork(Type T, string xml, bool getAvail)
        {
            XPathDocument doc = new XPathDocument(new StringReader(xml));
            XPathNavigator nav = doc.CreateNavigator();

            object rtn = T.GetConstructor(new Type[0]).Invoke(null);
            foreach (PropertyInfo pi in T.GetProperties())
            {
                foreach (Attribute pa in pi.GetCustomAttributes(typeof(XPathAttribute), true))
                {
                    XPathAttribute att = pa as XPathAttribute;

                    if (pi.PropertyType.Name == "List`1")
                    {
                        XPathNodeIterator nodes = nav.Select(att.XPath);
                        while (nodes.MoveNext())
                        {
                            XPathNavigator node = nodes.Current;
                            object list = pi.GetValue(rtn, null);
                            Type genericArgumentType = pi.PropertyType.GetGenericArguments()[0];
                            if (genericArgumentType.IsSubclassOf(typeof(Enum)))
                            {
                                pi.PropertyType.GetMethod("Add").Invoke(list, new object[] { EnumHelper.GetValue(genericArgumentType, node.Value) });
                            }
                            else if (genericArgumentType == typeof(string) || genericArgumentType.IsValueType)
                            {
                                pi.PropertyType.GetMethod("Add").Invoke(list, new object[] { node.ValueAs(genericArgumentType) });
                            }
                            else
                            {
                                pi.PropertyType.GetMethod("Add").Invoke(list, new object[] { FromXml(genericArgumentType, node.OuterXml) });
                            }
                        }
                    }
                    else
                    {
                        if (pi.PropertyType.IsSubclassOf(typeof(Enum)))
                        {
                            if (nav.SelectSingleNode(att.XPath) != null)
                            {
                                try
                                {
                                    pi.SetValue(rtn, EnumHelper.GetValue(pi.PropertyType, nav.SelectSingleNode(att.XPath).Value), null);
                                }
                                catch (KeyNotFoundException)
                                {
                                    Trace.WriteLine(string.Format("Missing enum value: {0} - {1}", pi.PropertyType.ToString(), nav.SelectSingleNode(att.XPath).Value));
                                }
                            }
                        }
                        else if (pi.PropertyType.IsValueType || pi.PropertyType == typeof(string))
                        {
                            if (nav.SelectSingleNode(att.XPath) != null)
                                pi.SetValue(rtn, nav.SelectSingleNode(att.XPath).ValueAs(pi.PropertyType), null);
                        }
                        else
                        {
                            if (nav.SelectSingleNode(att.XPath) != null)
                                pi.SetValue(rtn, FromXml(pi.PropertyType, nav.SelectSingleNode(att.XPath).OuterXml), null);
                        }
                    }
                }
            }

            return rtn;
        }

        #endregion

    }
}
